/* complete.c, Ait Emacs, Kevin Bloom, BSD 3-Clause, 2023-2024 */

#include <sys/stat.h>
#include "header.h"
#include "termbox.h"

/* basic filename completion, based on code in uemacs/PK */
int getfilename(char *prompt, char *buf, int nbuf)
{
  static char temp_file[] = TEMPFILE;
  int cpos = 0;  /* current character position in string */
  int k = 0, c, fd, didtry, iswild = 0, start_col = strlen(prompt);
  int isdir = TRUE;
  struct tb_event ev;
  char sys_command[255];
  FILE *fp = NULL;
  struct stat s;

//  buf[0] ='\0';
  cpos = strlen(buf);

  display_prompt_and_response(prompt, buf);
  for (;;) {
    didtry = (k == 0x09);  /* Was last command tab-completion? */
    tb_present();
    if(tb_poll_event(&ev) != TB_OK) return 0;

    if(msgline_editor(ev, prompt, buf, &cpos)) {
      k = 0;
      continue;
    }

    if(!ev.mod)
      k = ev.ch;
    else
      k = ev.key;

    if (k < 32 &&
        k != TB_KEY_CTRL_G &&
        k != TB_KEY_BACKSPACE &&
        k != TB_KEY_BACKSPACE2 &&
        k != TB_KEY_ENTER &&
        k != TB_KEY_CTRL_R &&
        k != TB_KEY_CTRL_S &&
        k != TB_KEY_ESC &&
        k != TB_KEY_CTRL_I &&
        k != TB_KEY_TAB)
      continue;

    switch(k) {
    case TB_KEY_CTRL_J: /* cr, lf */
    case TB_KEY_ENTER: {
      if(isdir) {
        didtry = 0;
        break;
      }
      /* backup files end with ~, therefore, don't tab if the ~ is at
         the end
      */
      char *idx = strchr(buf, '~');
      if (cpos > 0 && (idx != NULL && idx != &buf[strlen(buf)-1]))
        goto do_tab;
    }
    case TB_KEY_CTRL_G: /* ctrl-g, abort */
      if (fp != NULL) fclose(fp);
      tb_set_cursor(0, MSGLINE);
      clrtoeol("", MSGLINE);
      return (k != 0x07 && cpos > 0);

    case TB_KEY_CTRL_U: /* C-u kill */
      cpos = 0;
      buf[0] = '\0';
      break;

do_tab:
    case TB_KEY_TAB: /* TAB, complete file name */
      /* scan backwards for a wild card and set */
      iswild=0;
      while (cpos > 0) {
        cpos--;
        if (buf[cpos] == '*' || buf[cpos] == '?')
          iswild = 1;
      }

      /* first time retrieval */
      if (! didtry) {
        if (fp != NULL) fclose(fp);
        strcpy(temp_file, TEMPFILE);
        if (-1 == (fd = mkstemp(temp_file)))
          fatal("%s: Failed to create temp file\n");
        strcpy(sys_command, "printf \"%s\\n\" ");
        strcat(sys_command, buf);
        if (!iswild) strcat(sys_command, "*");
        strcat(sys_command, " >");
        strcat(sys_command, temp_file);
        strcat(sys_command, " 2>&1");
        (void) ! system(sys_command); /* stop compiler unused result warning */
        fp = fdopen(fd, "r");
        unlink(temp_file);
      }

      /* copy next filename into buf */
      while ((c = getc(fp)) != EOF && c != '\n')
        if (cpos < nbuf - 1 && c != '*')
          buf[cpos++] = c;

      buf[cpos] = '\0';

      if(stat(buf, &s) == 0 && s.st_mode & S_IFDIR) {
        buf[cpos++] = '/';
        isdir = TRUE;
      } else {
        isdir = FALSE;
      }

      buf[cpos] = '\0';
      for(int i = cpos+1; buf[i] != '\0'; i++)
        buf[i] = 0;
      if (c != '\n' || c == -1) rewind(fp);
      if(c == -1)  goto do_tab;
      didtry = 1;
      tb_set_cursor(start_col, MSGLINE);
      clrtoeol(prompt, MSGLINE);
      addstr(buf);
      break;

    default:
      if (cpos < nbuf - 1) {
        for(int i = strlen(buf); i > cpos; i--) {
          buf[i] = buf[i - 1];
        }
        buf[cpos] = k;
        buf[strlen(buf)] = '\0';
        tb_set_cursor(start_col, MSGLINE);
        addstr(buf);
        cpos++;
        tb_set_cursor(start_col + cpos, MSGLINE);
        isdir = k == '/';
      }
      break;
    }
  }
}

buffer_t * getbuffername(char *prompt, char *buf, int nbuf, int *ret)
{
  int cpos = 0;  /* current character position in string */
  int k = 0, c, didtry = 0;
  int start_col = strlen(prompt);
  int total_buffers = count_buffers() - 1;
  struct tb_event ev;
  buffer_t *bp = NULL;
  char tabbuffer[PATH_MAX];
  buf[0] ='\0';
  cpos = strlen(buf);

  display_prompt_and_response(prompt, buf);
  for (;;) {
    didtry = (k == 0x09) ? didtry + 1 : 0;  /* Was last command tab-completion? */
    if(k == 0x09 && didtry > total_buffers + 1) {
      didtry = 1;
    }
    tb_present();
    if(tb_poll_event(&ev) != TB_OK) return 0;

    if(msgline_editor(ev, prompt, buf, &cpos)) {
      k = 0;
      continue;
    }

    if(!ev.mod)
      k = ev.ch;
    else
      k = ev.key;

    switch(k) {
    case TB_KEY_CTRL_U: {
      char *list, *command, *data;
      FILE *pf;
      if(switch_command == NULL) {
        break;
      }
      list = calloc(total_buffers*sizeof(char *)*(STRBUF_L+1), '\0');
      bp = bheadp;
      strcpy(list, bp->b_bname);
      strcat(list, " ");
      bp = bp->b_next;
      for (; bp != NULL; bp = bp->b_next) {
        strcat(list, bp->b_bname);
        strcat(list, " ");
      }
      asprintf(&command, "printf \"%%s\\n\" %s | %s", list, switch_command);
      tb_shutdown();
      pf = popen(command,"r");
      if(pf == NULL){
        msg("Could not open pipe for output.");
        break;
      }
      data = malloc(sizeof(char *) * STRBUF_L);
      fgets(data, sizeof(char *) * STRBUF_L, pf);
      tb_init();
      LINES = tb_height();
      COLS = tb_width();
      MSGLINE = LINES-1;
      tb_set_input_mode(TB_INPUT_ALT);
      redraw();
      if(data[0] == -1 || data[0] == 0) {
        free(list);
        free(command);
        free(data);
        break;
      }
      for (bp = bheadp; bp != NULL; bp = bp->b_next) {
        /* data has a trailing \n character that we don't want to compare */
        if(strncmp(data, bp->b_bname, strlen(data) - 1) == 0) {
          free(list);
          free(command);
          free(data);
          *ret = 1;
          return bp;
        }
      }
      addstr(buf);
      break;
    }

    case TB_KEY_CTRL_J: /* cr, lf */
    case TB_KEY_ENTER:
      *ret = 1;
      return bp;
    case TB_KEY_CTRL_G: /* ctrl-g, abort */
      tb_set_cursor(0, MSGLINE);
      clrtoeol("", MSGLINE);
      return NULL;

    case TB_KEY_BACKSPACE2: /* del, erase */
    case TB_KEY_BACKSPACE: /* backspace */
      if (cpos == 0) continue;
      buf[--cpos] = '\0';
      tb_set_cursor(start_col + cpos, MSGLINE);
      addch(' ');
      tb_set_cursor(start_col + cpos, MSGLINE);
      break;

/*    case TB_KEY_CTRL_U:
      cpos = 0;
      buf[0] = '\0';
      break;
*/
    case TB_KEY_TAB: /* TAB, complete file name */
      if(didtry == 0) {
        strncpy(tabbuffer, buf, PATH_MAX);
        didtry++;
      }
      for (c = 1, bp = bheadp; bp != NULL; bp = bp->b_next) {
        if(tabbuffer[0] == '\0') {
          if (didtry == c) {
            strncpy(buf, bp->b_bname, PATH_MAX);
            break;
          }
          c++;
        }
        if(strcmp(tabbuffer, bp->b_bname) <= 0) {
          if(didtry == c) {
            strncpy(buf, bp->b_bname, PATH_MAX);
            break;
          }
          c++;
        }
      }
      cpos = 0;
      cpos += strlen(buf);
      buf[cpos] = '\0';
      for(int i = cpos+1; buf[i] != '\0'; i++)
        buf[i] = 0;
      tb_set_cursor(start_col, MSGLINE);
      clrtoeol(prompt, MSGLINE);
      addstr(buf);
      break;

    default:
      if (cpos < nbuf - 1) {
        for(int i = strlen(buf); i > cpos; i--) {
          buf[i] = buf[i - 1];
        }
        buf[cpos] = k;
        tb_set_cursor(start_col, MSGLINE);
        addstr(buf);
        cpos++;
        tb_set_cursor(start_col + cpos, MSGLINE);
      }
      break;
    }
  }
}
